/*
 * MembraneView.java
 *
 * Created on Jan 20, 2011, 6:42:14 PM
 *
 * Copyright (c) 2011 Artur Rataj.
 *
 * This code is distributed under the terms of the GNU Library
 * General Public License, either version 2 of the license or, at
 * your option, any later version.
 */

package pl.gliwice.iitis.spatialnn.network.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

import pl.gliwice.iitis.graphutils.Point;
import pl.gliwice.iitis.graphutils.view.*;
import pl.gliwice.iitis.spatialnn.network.Membrane;
import pl.gliwice.iitis.spatialnn.network.MembraneType;
import pl.gliwice.iitis.spatialnn.network.view.ExpressionView.ExpressionValue;

/**
 * A view of a membrane.
 * 
 * @author Artur Rataj
 */
public class MembraneView extends AbstractRectView {
    /**
     * Expression view, set the membrane's colors.
     */
    protected ExpressionView expression;
    /**
     * Stroke to draw the membrane.
     */
    Stroke stroke;
    /**
     * Stroke for drawing ticks.
     */
    Stroke tickStroke;
    
    /**
     * Creates a new instance of MembraneView. 
     * 
     * @param dm.res resolution, dpi
     * @param viewMin point in this view where the object's
     * coordinates are pointing to the left bottom corner
     * @param viewMax point in this view where the object's
     * coordinates are pointing to the top right corner
     */
    public MembraneView(DrawMode dm, Point viewMin, Point viewMax) {
        super(dm, viewMin, viewMax);
        expression = new ExpressionView(dm);
        if(dm.bw) {
            float[] dash = { 0.12f*dm.res, 0.03f*dm.res };
            stroke = new BasicStroke(0.05f*dm.res,
                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
                    1.0f, dash, 0.0f);
        } else
            stroke = new BasicStroke(0.12f*dm.res,
                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
        tickStroke = new BasicStroke(0.05f*this.dm.res);
    }
    @Override
    public void draw(Drawable object, Point min, Point max, Graphics2D g) {
        Membrane membrane = (Membrane)object;
        Point beg = toView(membrane.locBeg, min, max);
        Point end = toView(membrane.locEnd, min, max);
        double diffX = end.x - beg.x;
        double diffY = end.y - beg.y;
        double viewLength = Math.hypot(diffX, diffY);
        final int STEPS = 10;
        double e;
        if(viewLength/dm.res >= 1.0)
            e = 0;
        else
            e = Math.min(0.03, 0.05*(1.0 - viewLength/dm.res));
        final double DASH_START = 0.05 + e;
        final double DASH_STOP = 0.95 - e;
        ExpressionValue value = expression.getExpressionValue(Double.NaN);
        Stroke prevStroke = g.getStroke();
        g.setStroke(stroke);
        for(int i = 0; i < STEPS; ++i) {
            Point prev = new Point(
                    beg.x + (i + DASH_START)*(end.x - beg.x)/STEPS,
                    beg.y + (i + DASH_START)*(end.y - beg.y)/STEPS);
            Point curr = new Point(
                    beg.x + (i + DASH_STOP)*(end.x - beg.x)/STEPS,
                    beg.y + (i + DASH_STOP)*(end.y - beg.y)/STEPS);
            value.value = membrane.expressionBeg +
                    (i + DASH_START)*(membrane.expressionEnd - membrane.expressionBeg)/STEPS;
            if(dm.bw) {
                Point mid1 = segmentSide(prev, curr, 1.0/3.0, dm.res*0.05);
                Point mid2 = segmentSide(prev, curr, 2.0/3.0, -dm.res*0.05);
                g.draw(new Line2D.Double(
                        prev.x, prev.y, mid1.x, mid1.y));
                g.draw(new Line2D.Double(
                        mid1.x, mid1.y, mid2.x, mid2.y));
                g.draw(new Line2D.Double(
                        mid2.x, mid2.y, curr.x, curr.y));
            } else {
                expression.draw(value, null, g);
                Color color1 = g.getColor();
                value.value = membrane.expressionBeg +
                        (i + DASH_STOP)*(membrane.expressionEnd - membrane.expressionBeg)/STEPS;
                expression.draw(value, null, g);
                Color color2 = g.getColor();
                GradientPaint paint = new GradientPaint(
                        (float)prev.x, (float)prev.y, color1,
                        (float)curr.x, (float)curr.y, color2);
                g.setColor(Color.BLACK);
                g.setPaint(paint);
                g.draw(new Line2D.Double(
                        prev.x, prev.y, curr.x, curr.y));
            }
            prev = curr;
        }
        value.value = (membrane.expressionBeg + membrane.expressionEnd)/2.0;
        expression.draw(value, null, g);
        String text = membrane.key /* + membrane.expressionIndex */;
        Rectangle2D bounds = getStringBounds(g, text);
        double angle = Math.atan2(diffY, diffX);
        if(Math.abs(angle) > Math.PI/2) {
            Point tmp = beg;
            beg = end;
            end = tmp;
            angle = (angle + Math.PI)%(Math.PI*2);
        }
        double ratio = (viewLength/2.0 - bounds.getWidth()/2.0)/viewLength;
        int ascent = g.getFontMetrics().getAscent();
        double textBottom = 0.1*dm.res + ascent;
        double textTop = -0.13*dm.res;
        if(membrane.type == MembraneType.IN) {
            double t = textBottom;
            textBottom = textTop;
            textTop = t;
        }
        Point textPos = segmentSide(beg, end, ratio, textBottom);
        g.setColor(Color.BLACK);
        drawRotated(g, text, textPos, angle);
        g.setStroke(tickStroke);
        Color labelColor = g.getColor();
        for(double d = 0.0; d <= 1.0 + 1e-6; d += 0.5) {
            ratio = d;
            double extent;
            if(Math.abs(d - 0.5) > 1e-6)
                extent = 0.15;
            else
                extent = 0.06;
            Point s1 = segmentSide(beg, end, ratio, -extent*dm.res);
            Point s2 = segmentSide(beg, end, ratio, extent*dm.res);
            g.setColor(Color.BLACK);
            g.draw(new Line2D.Double(s1.x, s1.y, s2.x, s2.y));
            g.setColor(labelColor);
            if(false && (membrane.expressionBeg != 0.0 || membrane.expressionEnd != 1.0)) {
                double v = membrane.expressionBeg +
                        d*(membrane.expressionEnd - membrane.expressionBeg);
                text = formatter.format(v);
                bounds = getStringBounds(g, text);
                ratio = (d*viewLength - bounds.getWidth()/2.0)/viewLength;
                textPos = segmentSide(beg, end, ratio, textTop);
                drawRotated(g, text, textPos, angle);
            }
        }
        g.setStroke(prevStroke);
    }
}
